const fs = window.require('fs');

const test_report_version_size = 8;
const test_report_size_int = 4;
const test_report_size_long = 8;

function get_val(data, is_big_endian, offset, size) {
    let val = 0;

    if (is_big_endian) {
        for (let i = 0; i < size; i++) {
            val += data[offset + size - 1 - i] << (i * 8);
        }
    } else {
        for (let i = 0; i < size; i++) {
            val += data[offset + i] << (i * 8);
        }
    }

    return val;
}

const android_test_report_v1_0_content = {
    /* Report head */
    endianness_offset:          8,
    head_size_offset:           12,
    start_time_offset:          16,
    end_time_offset:            32,
    target_fps_offset:          48,
    cpu_cluster_num_offset:     56,
    cpu_cluster_core_num_offset:76,
    cpu_cluster_cores_offset:   92,
    cpu_cluster_core_block_size:48,
    cpu_max_freq_offset:        284,
    cpu_min_freq_offset:        300,
    gpu_max_freq_offset:        316,
    gpu_min_freq_offset:        320,
    data_block_count_offset:    328,
    data_block_size_offset:     332,
    data_mask_offset:           336,
    data_jank_offset:           340,
    data_big_jank_offset:       344,

    /* Report data block */
    data_block_offset:              352,
    data_block_size:                600,
    data_block_fps_offset:          0,
    data_block_frame_time_offset:   4,
    data_block_cpu_usage_offset:    516,
    data_block_cpu_freq_offset:     564,
    data_block_gpu_freq_offset:     584,
    data_block_temp_offset:         588,
    data_block_power_voltage_offset:592,
    data_block_power_current_offset:596,

    cpu_usage_high_threshold:       800,
    temp_high_threshold:            420,
};

function android_get_report_parser(data) {
    let version_byte = [];

    for (let i = 0; i < test_report_version_size; i++) {
        if (data[i] === 0)
            break;
        version_byte.push(data[i]);
    }

    let report_version = String.fromCharCode.apply(String, version_byte);
    if (report_version === 'v1.0')
        return android_test_report_v1_0_content;

    return null;
}

export function android_get_report_show_info(path) {
    let sys_data_file = path + '/system_data';
    let start_time = 0;
    let end_time = 0;
    let duration = 0;

    const sys_data = fs.readFileSync(sys_data_file);
    let report_parser = android_get_report_parser(sys_data);
    let is_big_endian = sys_data[report_parser.endianness_offset] === 1 ? true : false;
    start_time = get_val(sys_data, is_big_endian, report_parser.start_time_offset, test_report_size_long);
    end_time = get_val(sys_data, is_big_endian, report_parser.end_time_offset, test_report_size_long);
    duration = end_time - start_time;
    start_time *= 1000;

    return {start_time, duration};
}

function parse_report_head(data, report_parser, report_detail) {
    report_detail.start_time =
        get_val(data, report_detail.is_big_endian, report_parser.start_time_offset, test_report_size_long);
    report_detail.start_time *= 1000;

    report_detail.fps_target =
        get_val(data, report_detail.is_big_endian, report_parser.target_fps_offset, test_report_size_int);

    report_detail.cpu_cluster_num =
        get_val(data, report_detail.is_big_endian, report_parser.cpu_cluster_num_offset, test_report_size_int);

    let max_cpu_index = 0;
    for (let i = 0; i < report_detail.cpu_cluster_num; i++) {
        let cluster_cpu_num = get_val(data, report_detail.is_big_endian,
            report_parser.cpu_cluster_core_num_offset + (i * test_report_size_int), test_report_size_int);

        let cpu_index_array = [];
        for (let j = 0; j < cluster_cpu_num; j++) {
            let offset = report_parser.cpu_cluster_cores_offset + i * report_parser.cpu_cluster_core_block_size +
                j * test_report_size_int;
            let cpu_index = get_val(data, report_detail.is_big_endian, offset, test_report_size_int);
            if (cpu_index > max_cpu_index)
                max_cpu_index = cpu_index;
            cpu_index_array.push(cpu_index);
        }
        report_detail.cpu_index.push(cpu_index_array);

        let freq = get_val(data, report_detail.is_big_endian,
            report_parser.cpu_max_freq_offset + (i * test_report_size_int), test_report_size_int);
        report_detail.cpu_max_freq.push(freq);
        freq = get_val(data, report_detail.is_big_endian,
            report_parser.cpu_min_freq_offset + (i * test_report_size_int), test_report_size_int);
        report_detail.cpu_min_freq.push(freq);

        report_detail.cpu_freq.push([]);
    }

    max_cpu_index += 1;
    for (let i = 0; i < max_cpu_index; i++) {
        report_detail.cpu_usage.push([]);
        report_detail.cpu_average_usage.push(0);
        report_detail.cpu_high_load.push(0);
    }

    report_detail.gpu_max_freq =
        get_val(data, report_detail.is_big_endian, report_parser.gpu_max_freq_offset, test_report_size_int);
    report_detail.gpu_min_freq =
        get_val(data, report_detail.is_big_endian, report_parser.gpu_min_freq_offset, test_report_size_int);
}

function parse_fps_data(data, start_pos, report_parser, report_detail) {
    let fps = get_val(data, report_detail.is_big_endian, start_pos + report_parser.data_block_fps_offset, test_report_size_int);

    report_detail.fps.push(fps);
    report_detail.fps_average += fps;
    for (let i = 0; i < fps; i++) {
        let frameTime = get_val(data, report_detail.is_big_endian,
            start_pos + report_parser.data_block_frame_time_offset + i * test_report_size_int,
            test_report_size_int);
        if (frameTime > report_detail.fps_max_frame_time)
            report_detail.fps_max_frame_time = frameTime;
        report_detail.fps_frame_time.push(frameTime);
    }
}

function parse_cpu_data(data, start_pos, report_parser, report_detail) {
    for (let i = 0; i < report_detail.cpu_cluster_num; i++) {
        let cpu_freq_list = report_detail.cpu_freq[i];
        let freq = get_val(data, report_detail.is_big_endian,
            start_pos + report_parser.data_block_cpu_freq_offset + i * test_report_size_int, test_report_size_int);
        cpu_freq_list.push(freq);

        let cpu_index_list = report_detail.cpu_index[i];
        for (let j = 0; j < cpu_index_list.length; j++) {
            let cpu_usage_list = report_detail.cpu_usage[cpu_index_list[j]];
            let usage = get_val(data, report_detail.is_big_endian,
                start_pos + report_parser.data_block_cpu_usage_offset + cpu_index_list[j] * test_report_size_int,
                test_report_size_int);
            cpu_usage_list.push(usage / 10);
            report_detail.cpu_average_usage[cpu_index_list[j]] += usage;

            if (usage >= report_parser.cpu_usage_high_threshold)
                report_detail.cpu_high_load[cpu_index_list[j]] += 1;
        }
    }
}

function process_data_after_reading(report_detail) {
    report_detail.fps_average /= report_detail.data_block_num;
    for (let i = 0; i < report_detail.cpu_cluster_num; i++) {
        let cpu_index_list = report_detail.cpu_index[i];

        for (let j = 0; j < cpu_index_list.length; j++) {
            report_detail.cpu_average_usage[cpu_index_list[j]] /= ((report_detail.data_block_num - 1) * 10);
            report_detail.cpu_high_load[cpu_index_list[j]] =
                (report_detail.cpu_high_load[cpu_index_list[j]] / (report_detail.data_block_num - 1) * 100);
        }
    }

    report_detail.gpu_average_freq /= report_detail.data_block_num;
    report_detail.gpu_high_freq_percent = (report_detail.gpu_high_freq_percent / report_detail.data_block_num * 100);
    report_detail.temp_average /= report_detail.data_block_num;
    report_detail.temp_high_percent = (report_detail.temp_high_percent / report_detail.data_block_num * 100);
}

function parse_report_data(data, report_parser, report_detail) {
    report_detail.data_offset =
        get_val(data, report_detail.is_big_endian, report_parser.head_size_offset, test_report_size_int);

    if (report_detail.data_offset !== report_parser.data_block_offset)
        return;

    report_detail.data_block_num =
        get_val(data, report_detail.is_big_endian, report_parser.data_block_count_offset, test_report_size_int);

    if (report_detail.data_block_num === 0)
        return;

    let report_block_size =
        get_val(data, report_detail.is_big_endian, report_parser.data_block_size_offset, test_report_size_int);
    if (report_parser.data_block_size !== report_block_size)
        return;

    report_detail.fps_jank =
        get_val(data, report_detail.is_big_endian, report_parser.data_jank_offset, test_report_size_int);
    report_detail.fps_big_jank =
        get_val(data, report_detail.is_big_endian, report_parser.data_big_jank_offset, test_report_size_int);

    report_detail.data_mask =
        get_val(data, report_detail.is_big_endian, report_parser.data_mask_offset, test_report_size_int);

    let start_pos = report_detail.data_offset;
    for (let i = 0; i < report_detail.data_block_num; i++) {
        parse_fps_data(data, start_pos, report_parser, report_detail);
        parse_cpu_data(data, start_pos, report_parser, report_detail);

        let gpu_freq =
            get_val(data, report_detail.is_big_endian, start_pos + report_parser.data_block_gpu_freq_offset, test_report_size_int);
        report_detail.gpu_freq.push(gpu_freq);
        report_detail.gpu_average_freq += gpu_freq;
        if (gpu_freq > report_detail.gpu_max_freq)
            report_detail.gpu_max_freq = gpu_freq;
        if (gpu_freq < report_detail.gpu_min_freq)
            report_detail.gpu_min_freq = gpu_freq;
        if (gpu_freq === report_detail.gpu_max_freq)
            report_detail.gpu_high_freq_percent++;

        let temp =
            get_val(data, report_detail.is_big_endian, start_pos + report_parser.data_block_temp_offset, test_report_size_int);
        report_detail.temp.push((temp / 10).toFixed(1));
        report_detail.temp_average += temp;
        if (temp > report_detail.temp_max)
            report_detail.temp_max = temp;
        if (temp < report_detail.temp_min)
            report_detail.temp_min = temp;
        if (temp >= report_parser.temp_high_threshold)
            report_detail.temp_high_percent++;

        start_pos += report_parser.data_block_size;
    }

    process_data_after_reading(report_detail);
}

export function android_get_report_detail_info(path, report_detail) {
    let sys_data_file = path + '/system_data';
    const sys_data = fs.readFileSync(sys_data_file);
    let report_parser = android_get_report_parser(sys_data);
    report_detail.is_big_endian = sys_data[report_parser.endianness_offset] === 1 ? true : false;

    parse_report_head(sys_data, report_parser, report_detail);
    parse_report_data(sys_data, report_parser, report_detail);
}